ConquerMe II: The Lost Princess
by MisterE
Tutorial by Lucifer48 [Immortal Descendants]
(August 8th, 1999)
Contents:
Battle One: One yer Way!
Battle Two: The Tower
The Final Battle: The Princess
Conclusion & Greetings
Battle One: One yer Way!
XXXX:004015E5 CALL USER32!SendDlgItemMessageA ;(WM_GETTEXTLENGTH)
XXXX:004015EA CMP EAX,10 ;16 chars for the serial
... ;(the 16th char of the serial is not used)
XXXX:00401602 CMP EAX,08
XXXX:00401605 JAE 0040161B ;8 chars minimum for the name (otherwise ..
... ; .. add null chars to arrive at 8 chars)
XXXX:0040161B CALL 004012DE ;protection scheme
XXXX:00401620 PUSH 004044A8 ;11111111
XXXX:00401625 PUSH 004044E2 ;8 numbers (crypted)
XXXX:0040162A CALL KERNEL32!lstrcmp ;comparaison
XXXX:0040162F OR EAX,EAX
The call 004012DE is made by few following loops. Let's note X0, X1, X2, ..., Xe the 15
chars of the serial; and N0, N1, ..., N7 the 8 chars of the name.
This is the first loop:
XXXX:00401353 PUSH ECX ;LOOP #1 (ecx is the index of the loop)
XXXX:00401354 MOV AL,[ECX+004046E3] ;read X0, X1, ..., X7
XXXX:0040135A MOV DL,[ECX+00404AE5] ;read N0, N1, ..., N7
XXXX:00401360 SUB AX,DX
XXXX:00401363 MOV DL,[ECX+00404AF4] ;read X7, X9, ..., Xe
XXXX:00401369 SUB AX,DX
XXXX:0040136C ADD AX,012C
XXXX:00401370 MOV BL,0A
XXXX:00401372 CWD
XXXX:00401374 IDIV BX
XXXX:00401377 ADD ESI,EDX ;esi will contain a sort of checksum
XXXX:00401379 OR DL,30
XXXX:0040137C POP ECX
XXXX:0040137D MOV [ECX+0040449F],DL ;write the final char
XXXX:00401383 INC ECX
XXXX:00401384 CMP ECX,08
XXXX:00401387 JNZ 00401353
This loop makes a pretty serial of 8 chars (let's note it S0, S1, ..., S7).
(X0 - N0 - X7 + 12Ch) mod Ah
+ (X1 - N1 - X8 + 12Ch) mod Ah
+ ...
+ (X7 - N7 - Xe + 12Ch) mod Ah
-------------------------------
= ESI
We continue:
XXXX:004013B2 MOV DL,[004046E3] ;read X0
XXXX:004013B8 AND DL,0F
XXXX:004013BB ADD ESI,EDX ;definitive value for esi
XXXX:004013BD XOR EDX,EDX
XXXX:004013BF MOV EAX,ESI
XXXX:004013C1 MOV CL,08
XXXX:004013C3 CWD
XXXX:004013C5 IDIV CX
XXXX:004013C8 XOR ESI,ESI
The result (of mod) is in EDX, et will disturb the copy of the string X0, X1, ..., X7; it will do that:
XXXX:004013CC MOV AL,[ESI+004046E3] ;read X0, X1, X2, ..., X7
XXXX:004013D2 MOV [EDX+004044E2],AL ;copy (in a wrong order)
XXXX:004013D8 INC EDX
XXXX:004013D9 CMP EDX,08
XXXX:004013DC JNZ 004013E3
XXXX:004013DE MOV EDX,00000000
XXXX:004013E3 INC ESI
XXXX:004013E4 CMP ESI,08
XXXX:004013E7 JNZ 004013CC
Example: EDX=5, then
X0 X1 X2 X3 X4 X5 X6 X7 ==[become]==> X3 X4 X5 X6 X7 X0 X1 X2
We continue:
XXXX:004013F5 PUSH ECX ;LOOP #2
XXXX:004013F6 XOR EDX,EDX
XXXX:004013F8 XOR EAX,EAX
XXXX:004013FA MOV DL,[ECX+0040449F] ;read S0, S1, ..., S7
XXXX:00401400 SUB AX,DX
XXXX:00401403 ADD AX,012C
XXXX:00401407 MOV BL,0A
XXXX:00401409 CWD
XXXX:0040190B IDIV BX
XXXX:0040140E MOV AL,[ECX+004044E2] ;read X0, X1, ..., X7 (in the WRONG order)
XXXX:00401414 AND AL,0F
XXXX:00401416 ADD AL,0A
XXXX:00401418 SUB AL,DL
XXXX:0040141A CMP AL,09
XXXX:0040141C JBE 00401420
XXXX:0040141E SUB AL,0A
XXXX:00401420 OR AL,30
XXXX:00401422 MOV [ECX+004046E3],AL :we will obtain 8 numbers again
XXXX:00401428 POP ECX ;let's note it T0, T1, ..., T7
XXXX:00401429 INC ECX
XXXX:0040142A CMP ECX,08
XXXX:0040142D JNZ 004013F5
The next boucle is exactly the same as the LOOP #1 except, that T0, T1, ...T7 are read (instead of N0, N1, ..., N7):
XXXX:00401439 PUSH ECX
XXXX:0040143A MOV AL,[ECX+004044E2] ;read X0, X1, ..., X7 (in the WRONG order)
XXXX:00401440 MOV DL,[ECX+004046E3] ;read T0, T1, ..., T7
XXXX:00401446 SUB AX,DX
XXXX:00401449 MOV DL,[ECX+00404AF4] ;read X7, X9, ..., Xe
...
exactly the same as the LOOP #1
...
XXXX:00401463 MOV [ECX+004044E2],DL ;we'll get again 8 numbers
XXXX:00401469 INC ECX ;let's note it U0, U1, ..., U7
XXXX:0040146A CMP ECX,08
we continue...
... ;few unused lines...
XXXX:00401498 MOV DL,[004044E2] ;read U0
XXXX:0040149E AND DL,0F
XXXX:004014A1 ADD ESI,EDX ;same method as above
XXXX:004014A3 XOR EDX,EDX
XXXX:004014A5 MOV EAX,ESI ;re-use the ckecksum of the previous loop
XXXX:004014A7 MOV CL,08
XXXX:004014A9 CWD
XXXX:004014AB IDIV CX ;edx= eax mod 8
XXXX:004014AE XOR ESI,ESI
The remainder (of the division) will be used (exactly as above)...
XXXX:004014B2 MOV AL,[ESI+004044E2] ;read U0, U1, ..., U7
XXXX:004014B8 MOV [EDX+004046E3],AL ;copy (in a WRONG order)
XXXX:004014BE INC EDX ;(same thing as above...)
XXXX:004014BF CMP EDX,08
XXXX:004014C2 JNZ 004014C9
XXXX:004014C4 MOV EDX,00000000
XXXX:004014C9 INC ESI
XXXX:004014CA CMP ESI,08
XXXX:004014CD JNZ 004014B2
The last loop:
XXXX:004014DB PUSH ECX ;exactly the same as the LOOP #2
...
XXXX:004014E0 MOV DL,[ECX+00404AF4] ;read X7, X9, ..., Xe
...
XXXX:004014F4 MOV AL,[ECX+004046E3] ;read U0, U1, ..., U7 (in a WRONG order)
...
XXXX:00401508 MOV [ECX+004044E2],AL ;our 8 final chars (*)
XXXX:0040150E POP ECX
XXXX:0040150F INC ECX ;next loop
XXXX:00401510 CMP ECX,08
XXXX:00401513 JNZ 004014DB
(*) This number is compared to 11111111.
Let's find now a valid serial... Each chars are independant, it is will be easier for us.
This is what we must find for the first char (X0):
L1: { ( (X0 - N0 - X7 +12C ) mod Ah ) OR 30h = S0
L2: { ( ( (X0' AND F) + Ah ) - ( (-S0 + 12C) mod Ah ) ) OR 30h = T0 (**)
L3: { ( (X0' - T0 - X7 + 12C) mod A ) OR 30h = U0
L4: { ( ( (U0' AND F) + Ah ) - ( (-X7 + 12C) mod Ah ) ) OR 30h = 31h (***)
Remark: I've written X0', the X0 byte after the re-ordering/copy loop (in XXXX:004013CC),
same thing for U0'.
(see XXXX:004014AB and XXXX:004013C5).
(**): Don't forget that if T0>39h then T0=T0-Ah; same thing for (***).
Let's analyse the fourth line (L4):
we must have ((U0' AND F) + Ah) - ((-X7 + 12C) mod Ah) = 1. Without forgetting the condition
(**), we have six possibilities:
(U0' AND F) + Ah) = Fh and ((-X7 + 12C) mod Ah) = 4
or (U0' AND F) + Ah) = Eh and ((-X7 + 12C) mod Ah) = 3
or (U0' AND F) + Ah) = Dh and ((-X7 + 12C) mod Ah) = 2
or (U0' AND F) + Ah) = Ch and ((-X7 + 12C) mod Ah) = 1
or (U0' AND F) + Ah) = Bh and ((-X7 + 12C) mod Ah) = 0
or (U0' AND F) + Ah) = Ah and ((-X7 + 12C) mod Ah) = 9
And so:
(U0' AND F) + Ah = {Fh, Eh, Dh, Ch, Bh ,Ah}
<=> (U0' AND F) = {5, 4, 3, 2, 1, 0}
<=> U0' = {35h, 34h, 33h, 32h, 31h, 30h}
The chars U0, U1, ..., U7 are compulsorily included between 30h and 35h.
We use the same process for the second line (L2).
S0, S1, ..., S7 can contain the following chars: {"0", "1", "2", "3", "8", "9", ":", ";", "<", "="}.
But with having a quick look at the first line (L1), we see that S0, S1, ..., S7 are the result
of a mod Ah, so the chars S0, S1, ..., S7 are compulsorily one of this: {"0", "1", "2", "3", "8", "9"}
With these few restrictions, we can try to produce couple/pair (X0-X7, ..., X7-Xe).
See my source written in C; for each 8 letters of the name, we will find couples.
Remark: The 8th character is used two times (couple X0-X7 and X7-Xe),
so you should be careful. The 6 other couples can be taken as you want.
example: _123456_789abc_
(warning: the two couples X0-X7 and X7-Xe have X7 in commun!)
What is doing the source?
It computes possible couples for each letter of your name; by taking 8 couples it may work!
Why it may: because my source assumes that there is no re-ordering/copy loop; X?=X?' et U?=U?'.
(see in XXXX:004014AB et XXXX:004013C5). In my view, it is the easiest way to succeed this battle.
And then, we have to find couples which gives EDX=0 (other value of edx are possible) in XXXX:004013CC et XXXX:004014B8.
In order not to change the order for X0, X1, ..., X7 (same thing for U0, U1, ..., U7).
How finding a valid serial?
Firstly, you must pass the first re-ordering/copy loop, for this, use your feeling, try to make the
8 first characters cyclic (example: 48484848), or with few times the same character. When you have
passed the first re-ordering/copy loop, change the 7 last chars to pass the second (edx=0 in
XXXX:004014AB; or edx=? if U0=U0', ..., U7=U7').
For my name, i take eight working couples:
0 2 ;couple for the letter "L"
0 X ;couple for the letter "u"
0 8 ;couple for the letter "c"
4 8 ;couple for the letter "i"
1 8 ;couple for the letter "f"
0 9 ;couple for the letter "e"
4 X ;couple for the letter "r"
e n ;couple for the letter "4"
-----------------
0004104e2X8889Xn
You add to this serial a 16th char which is not used by the protection scheme, et you get few
good serials (Name/ Lucifer48):
0004104e2X8889n*
0004104e2X8889d*
0004104e2X8889Z*
0004104e2X8889P*
0004104e2X8889F*
`004104e2X8889x*
...
`004104e2X8889F*
* any character
Status: registered!!
Battle Two: The Tower
XXXX:00401834 PUSH 004046E3 ;address of buffer
XXXX:00401839 PUSH EAX ;length of serial -1 (see remark)
XXXX:0040183A PUSH 0D ;WM_GETTEXT
XXXX:0040183C PUSH 000000CB ;serial edit box
XXXX:00401841 PUSH DWORD PTR [EBP+08] ;handle of the dialog box
XXXX:00401844 CALL USER32!SendDlgItemMessageA
XXXX:00401849 CALL 0040168B ;protection scheme
XXXX:0040184E CMP BYTEP PTR [004044E0],01
XXXX:00401855 JNZ 0040187D ;jmp = bad serial
Remark: The last char (key) pressed is not read, because the buffer have the size of EAX
(EAX is the length of the serial gotten with WM_GETTEXTLENGTH just before); in fact the length of
buffer is EAX-1; the last byte is reserved because it is null; so we must add another char
after the serial to get the good length. In my view, it is wanted by the author because the
name is read correcly (XXXX:00401806 INC EAX).
Let's note X0, X1, ..., X9 the chars of the serial; and N0, N1, ..., N8 the chars of the
name.
In the call 0040168B:
... (eax=ebx=ecx=edx=0)
XXXX:00401693 MOV AL,[004046E3] ;X0
XXXX:00401698 XOR AL,[004046EC] ;X9
XXXX:0040169E XOR AH,AL ;AL=AH
XXXX:004016A0 XOR AX,[004046E4] ;X2 X1
XXXX:004016A7 XOR EAX,[004046E6] ;X6 X5 X4 X3
XXXX:004016AD XOR EAX,[004046E8] ;X8 X7 X6 X5
XXXX:004016B3 XOR EAX,[00404AE5] ;N3 N2 N1 N0
XXXX:004016B9 XOR EAX,[00404AE8] ;N6 N5 N4 N3
XXXX:004016BF XOR AX,[00404AEC] ;N8 N7
XXXX:004016C6 MOV ECX,EAX ;save the result for later use
XXXX:004016C8 MOV EBX,00000029
XXXX:004016CD CWD
XXXX:004016CF IDIV EBX
XXXX:004016D1 MOV EAX,EDX
XXXX:004016D3 MOV EBX,00000009
XXXX:004016D8 CWD
XXXX:004016DA IDIV EBX
XXXX:004016DC MOV EAX,EDX
So EAX= (EAX mod 41d) mod 9; we have 9 possibilities:
EAX=0: EAX = (ECX mod 7) + 1 et ECX = ECX XOR 00BC614E (BC614Eh=12345678d)
EAX=1: ECX = ECX XOR 0023CACE ;23CACEh = 2345678d
EAX=2: ECX = ECX XOR 0005464E ; 5464Eh = 345678d
EAX=3: ECX = ECX XOR 0000B26E ; B26Eh = 45678d
EAX=4: ECX = ECX XOR 0000162E ; 162Eh = 5678d
EAX=5: ECX = ECX XOR 000002A6 ; 2A6h = 678d
EAX=6: ECX = ECX XOR 0000004E ; 4Eh = 78d
EAX=7: ECX = ECX XOR 00000008 ; 8h = 8d
EAX=8: ECX = ECX XOR 00000000
If the name is not long enough, we can use the fact that the missing byte(s) is null (the buffer is long enough).
XXXX:00401743 CMP EAX,36F95001
XXXX:00401749 SETZ AL
XXXX:0040174C MOV [004044E0],AL
XXXX:00401751 RET
-
Assuming that the result of the double mod is: 8
then in XXXX:004016C6, we must find: 36F95001
but (36F95001h mod 41d) mod 9 = 2
this is impossible.
-
Assuming that the result of the double mod is: 7
then in XXXX:004016C6, we must find: 36F95001 XOR 8 = 36F95009
but (36F95009h mod 41d) mod 9 = 5
-
Assuming that the result of the double mod is: 6
then in XXXX:004016C6, we must find: 36F95001 XOR 4E = 36F9504F
but (36F9504Fh mod 41d) mod 9 = 7
-
Assuming that the result of the double mod is: 5
then in XXXX:004016C6, we must find: 36F95001 XOR 2A6 = 36F952A7
but (36F952A7h mod 41d) mod 9 = 1
-
Assuming that the result of the double mod is: 4
then in XXXX:004016C6, we must find: 36F95001 XOR 162E = 36F9462F
but (36F9462Fh mod 41d) mod 9 = 7
-
Assuming that the result of the double mod is: 3
then in XXXX:004016C6, we must find: 36F95001 XOR B26E = 36F9E26F
but (36F9E26Fh mod 41d) mod 9 = 0
-
Assuming that the result of the double mod is: 2
then in XXXX:004016C6, we must find: 36F95001 XOR 5464E = 36FC164F
but (36FC164Fh mod 41d) mod 9 = 0
-
Assuming that the result of the double mod is: 1
then in XXXX:004016C6, we must find: 36F95001 XOR 23CACE = 36DA9ACF
but (36DA9ACFh mod 41d) mod 9 = 7
So we conclude that in XXXX:004016C6, ecx mod 41d = { 36d, 27d, 18d, 9d, 0d }.
We execute the case EAX=0, to get a new value (between 1 to 8) of EAX.
And we will restart the same process as above, just after passed the case EAX=0.
Result to find: 36F95001 XOR 00BC614E = 3645314F
But before finding 3645314F, don't forget that we must pass the mod test (by having a
null remainder). We see, there are two possibilities (stay 3 and 2; see above).
Then:
-
Assuming that the result of the double mod is: 3
then in XXXX:004016FD, we must find: 3645314F XOR B26E = 36458321
but (36458321h mod 7) + 1 = 1
that's wrong!
-
Assuming that the result of the double mod is: 2
then in XXXX:004016FD, we must find: 3645314F XOR 5464E = 36407701
but (36407701h mod 7) + 1 = 2 (YES!!!)
I summarize:
- In XXXX:004016C6 i have ECX = 36407701
- (36407701h mod 41d) mod 9 = 0 (in eax)
- 36407701 XOR 00BC614E = 36FC164F (XXXX:004016FD)
- (36407701h mod 7) + 1 = 2
- I have ECX = 36FC164F (and EAX=2)
- 36FC164F XOR 5464Eh = 36F95001 (= result to find!!)
So the only possible dword (in XXXX:004016C6) is: 36407701.
The following is very classical (found in many crackmes).
I remove (at first) the mask of my name ( 6963754Ch XOR 72656669h XOR 00003834 = 1B062B11 ).
36407701 XOR 1B062B11 = 2D465C10
It stays now to solve this:
00 00 X0 X0
xor 00 00 X9 X9
xor 00 00 X2 X1
xor X6 X5 X4 X3
xor X8 X7 X6 X5
-----------
2D 46 5C 10
I take as example (i try to solve it simply):
X1=X9=X0= 30h ("0")
X3= 50h ("P")
X5= 70h ("p")
X4= 2Dh ("-")
X6= 41h ("A")
X2= 30h ("0")
X5 XOR 46 = X7 so X7= 36h ("6")
X6 XOR 2D = X8 so X8= 6Ch ("l")
By putting the chars together, we get the serial: 000P-pA6l0
You add a last dummy char; we get this:
Name/ Lucifer48
Serial/ 000P-pA6l0*
(* any character)
or better:
Serial/ #00P-pA6l#*
(* any character; # any character too)
There are many other possibilities...
Status: registered!!
The Final Battle: The Princess
XXXX:0040270D CMP BYTE PTR [004044E1],01 ;good combination of the check boxes ?
XXXX:00402714 JNZ 00402748
XXXX:00402716 PUSH 00 ;MB_OK
XXXX:00402718 PUSH 0040446D ;You have succeeded!
XXXX:0040271D PUSH 004043D6 ;end message
XXXX:00402722 PUSH DWORD PTR [EBP+08] ;handle
XXXX:00402725 CALL USER32!MessageBoxA
XXXX:0040272A PUSH FF
XXXX:0040272C PUSH DWORD PTR [EBP+08]
XXXX:0040272F CALL USER32!EndDialog
XXXX:00402734 JMP 00402748
If we have put the check boxes correctly; then, [004044E1] is set to 1 (just before the crackme
is checking if the edit box isn't empty) and then, a new dialog box is created, and you can see the
princess in front of you. Wow !!!
The easiest combination is to press 1 single button:
O O O O
O o o O
O ___ O
O X <------ this check box
O O O O
XXXX:004011F8 CALL USER32!DialogBoxParamA ;dialog box of the crackme 3
XXXX:004011FD CMP BYTE PTR [004044E1],01 ;see comment below
XXXX:00401204 JNZ 00401221
XXXX:00401206 MOV EAX,004018B5
...
XXXX:00401216 PUSH DWORD PTR [004044D4]
XXXX:0040121C CALL USER32!DialogBoxParamA ;display the princess
XXXX:00401221 JMP 004012D8
In fact, to arrive to the MessageBox, there is a patch to do:
XXXX:004026C7 CMP AX,013F ;control ID of "OK" button
XXXX:004026CB JNZ 0040274A
XXXX:004026CD SHR EAX,10 ;EAX=00000000
XXXX:004026D0 OR AX,AX
XXXX:004026D3 JNZ 00402781 ;ZF is set (no jump)
XXXX:004026D9 CMP EAX,-01 ;it's IMPOSSIBLE!!!
XXXX:004026DC JNZ 00402736
Is that a allusion for: you cannot see the blade ???
Conclusion & Greetings
Finaly, i did it ! The first battle is really the most difficult. The second was ... classical;
and the final battle: a little strange ! However, it was a bit tougher than ConquerMe I.
Awaiting (maybe) ConquerMe III (coded in asm of course...); i salute you!
Greetings: All ID members (Volatility, Torn@do, ...), Eternal Bliss, ACiD BuRN, LaZaRuS,
Duelist, people on #cracking4newbies, ..., and YOU MisterE for creating two excellent crackmes !!!
(c) Lucifer48. All rights reversed